#pragma once
#include <iostream>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace log_ns;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERR
};

const static int gport = 8888;
const static int gsock = -1;
const static int gblcklog = 8;

using command_service_t = std::function<void(int sockfd, InetAddr addr)>;

class TcpServer
{
public:
    TcpServer(command_service_t service, uint16_t port = gport)
        : _port(port),
          _listensockfd(gsock),
          _isrunning(false),
          _service(service)
    {
    }
    void InitServer()
    {
        // 1. 创建socket
        _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(FATAL, "socket create error\n");
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket create success, sockfd: %d\n", _listensockfd); // 3

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        // 2. bind sockfd 和 Socket addr
        if (::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            LOG(FATAL, "bind error\n");
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");

        // 3. 因为tcp是面向连接的，tcp需要未来不断地能够做到获取连接
        if (::listen(_listensockfd, gblcklog) < 0)
        {
            LOG(FATAL, "listen error\n");
            exit(LISTEN_ERR);
        }
        LOG(INFO, "listen success\n");
    }
    class ThreadData
    {
    public:
        int _sockfd;
        TcpServer *_self;
        InetAddr _addr;
    public:
        ThreadData(int sockfd, TcpServer *self, const InetAddr &addr):_sockfd(sockfd), _self(self), _addr(addr)
        {}
    };
    void Loop()
    {
        // signal(SIGCHLD, SIG_IGN);
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // 4. 获取新连接
            int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                LOG(WARNING, "accept error\n");
                continue;
            }
            InetAddr addr(client);
            LOG(INFO, "get a new link, client info : %s, sockfd is : %d\n", addr.AddrStr().c_str(), sockfd);

            // version 2 ---- 多线程版本 --- 不能关闭fd了，也不需要了
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this, addr);
            pthread_create(&tid, nullptr, Execute, td); // 新线程进行分离
        }
        _isrunning = false;
    }
    static void *Execute(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->_self->_service(td->_sockfd, td->_addr);
        ::close(td->_sockfd);
        delete td;
        return nullptr;
    }
    // void Service(int sockfd, InetAddr addr)
    // {
    //     // 长服务
    //     while (true)
    //     {
    //         char inbuffer[1024]; // 当做字符串
    //         ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1);
    //         if (n > 0)
    //         {
    //             inbuffer[n] = 0;
    //             LOG(INFO, "get message from client %s, message: %s\n", addr.AddrStr().c_str(), inbuffer);

    //             std::string echo_string = "[server echo] #";
    //             echo_string += inbuffer;
    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if (n == 0)
    //         {
    //             LOG(INFO, "client %s quit\n", addr.AddrStr().c_str());
    //             break;
    //         }
    //         else
    //         {
    //             LOG(ERROR, "read error: %s\n", addr.AddrStr().c_str());
    //             break;
    //         }
    //     }
    // }

    ~TcpServer() {}

private:
    uint16_t _port;
    int _listensockfd;
    bool _isrunning;

    command_service_t _service;
};

// pthread_self() pthread_self() 是一个 POSIX 线程库函数，它返回调用线程的线程 ID。
// 这个函数的返回值是当前线程的 ID（在多线程程序中每个线程都有一个唯一的 ID）。在这行代码中，pthread_self() 获取的是当前执行 Execute 函数的线程的 ID。
// pthread_detach(pthread_self()) pthread_detach() 函数的作用是将指定线程（通过其线程 ID）标记为“分离状态”。
// 分离状态的线程意味着：
// 线程退出后，它的所有资源（如内存和其他线程状态）都会被操作系统自动回收。
// 线程退出后，无法使用 pthread_join() 来等待该线程的退出状态或获取返回值。
// 一旦线程结束，所有的资源会被操作系统回收，避免出现“僵尸线程”。

// 这段代码实现了一个基本的 TCP 服务器 类 (TcpServer)，通过多线程处理客户端的连接。
// 常量定义
// 定义了一些常量，用于表示不同阶段可能发生的错误类型（如 SOCKET_ERROR, BIND_ERROR, LISTEN_ERR）
// gport：默认的服务器端口号 8888。
// gsock：默认的套接字文件描述符值，表示未初始化的套接字。
// gblcklog：listen() 系统调用的 backlog（最大连接数），即服务器在开始接收连接之前，内核的连接队列最大容量。
// command_service_t 类型定义
// 定义了一个类型别名 command_service_t，它是一个 std::function 类型，表示一个接受两个参数（sockfd 和 addr）并返回 void 的函数。这个函数在实际的应用中将用于处理每个客户端的连接请求。
// TcpServer 类构造函数
// command_service_t service：构造函数接收一个处理客户端请求的回调函数 service，即每当一个客户端连接建立后，服务器就调用该函数来处理连接。
// uint16_t port：默认端口为 8888，服务器将监听该端口。
// 初始化服务器 (InitServer 方法)
// 创建 Socket：使用 ::socket() 创建一个 TCP 套接字 (SOCK_STREAM)。
// 如果创建失败（socket 返回值小于 0），日志记录错误并退出程序。
// 绑定 Socket 地址：通过 ::bind() 将创建的套接字与指定的端口和 IP 地址绑定。
// 服务器绑定到本机所有可用 IP 地址 (INADDR_ANY)，并监听指定的端口。
// 监听连接：通过 ::listen() 开始监听传入的连接，gblcklog 参数指定了等待连接的最大队列长度。
// ThreadData 类
// ThreadData：用于保存每个客户端连接的信息，包含：
// _sockfd：客户端的套接字文件描述符。
// _self：指向当前 TcpServer 实例的指针。
// _addr：客户端的地址信息（封装在 InetAddr 对象中）。
// Loop 方法
// Loop：这是服务器的主循环，主要功能是：
// 使用 ::accept() 接受新的客户端连接。
// 每当接收到一个新连接时，使用 pthread_create 创建一个新线程，线程通过 Execute 函数处理客户端的请求。
// 每个连接的信息（套接字和客户端地址）封装在 ThreadData 中传递给线程。
// pthread_create：创建一个新线程来处理客户端的请求。线程通过 Execute 方法执行指定的服务函数。
// Execute 方法
// Execute：线程的入口函数，执行以下操作：
// 使用 pthread_detach(pthread_self()) 将线程设为分离状态，确保线程结束后自动回收资源。
// 调用传入的回调函数 td->_self->_service(td->_sockfd, td->_addr)，即执行传递给服务器的具体业务逻辑（如处理客户端请求）。
// 关闭客户端连接的套接字。
// 删除 ThreadData 对象。
